import newPrototype from "./array";
import Dep from "./dep";

class Observer {
	constructor(data) {
		this.dep = new Dep();
		Object.defineProperty(data, '__ob__', {
			value: this,
			enumerable: false
		})
		if (Array.isArray(data)) {
			data.__proto__ = newPrototype;
			this.observeArray(data);
		} else {
			this.walk(data);
		}
	}
	// 数组
	observeArray (data) {
		data.forEach(item => observe(item));
	}

	// 对象
	walk (data) {
		let keys = Object.keys(data);
		keys.forEach(key => {
			// 监听属性
			defineReactive(data, key, data[key])
		})
	}
}

// Object.defineProperty(obj, prop, descriptor)
function defineReactive (data, key, value) {
	// observe(value);
	let childOb = observe(value);

	let dep = new Dep(); // 为每个属性实例化一个 Dep

	Object.defineProperty(data, key, {
		get () {
			// 依赖收集 —— 页面取值的时候，可以把 watcher 收集到 dep 里面
			if (Dep.target) {
				// 如果有 watcher，dep 就会保存 watcher 同时watcher 也会保存 dep
				dep.depend();
				if (childOb) {
					childOb.dep.depend();
					if (Array.isArray(value)) {
						// 如果内部还是数组
						if (Array.isArray(value)) {
							dependArray(value); // 不停地进行依赖收集
						}
					}
				}
			}
			return value;
		},
		set (newValue) {
			if (newValue === value) return;
			observe(newValue);
			value = newValue;
			dep.notify(); // 通知渲染 watcher 去更新
		}
	})
}

function dependArray (value) {
	for (let e, i = 0, l = value.length; i < l; i++) {
		e = value[i];
		// e.__ob__代表e已经被响应式观测了 但是没有收集依赖 所以把他们收集到自己的Observer实例的dep里面
		e && e.__ob__ && e.__ob__.dep.depend();
		if (Array.isArray(e)) {
		  // 如果数组里面还有数组  就递归去收集依赖
		  dependArray(e);
		}
	}
}

export function observe (data) {
	if (typeof data !== 'object' || data == null) {
		return;
	}
	if (data.__ob__) {
		return data;
	}
	return new Observer(data);
}